#ifndef ROOTBOX_H_JM_20120710
#define ROOTBOX_H_JM_20120710

#include "GenericBoxModel.h"
#include "BaseBoxDescriptor.h"

namespace jm4bbx {


	/**
	 * Nadstavbova bazova trieda pre boxy, rozsiruje chobanie boxov o tzv. 
	 * signaly. Signaly su klasicke spojenia boxov (via), ktore nenesu data.
	 * Takteto boxy mozu mat 0..* vstupnych signalov a 0..* vystupnych. 
	 * Pocet vstupnych a vystupnych signalov nemusi byt zhodny. Boxy 
	 * naviac mozu definovat klasicky spojenia ako je bezne.
	 * 
	 * Signaly su urcene pomocou parametrov boxu v definicii modelu.
	 * Jedna sa o parametre definovane v {@link BaseBoxDescriptor}, konkretne
	 * SIGNAL_IN_PARAMETER_NAME a SIGNAL_OUT_PARAMETER_NAME. Cislena hodnota 
	 * predana v parametre znaci pocet signalov daneho typu.
	 * 
	 * Zdedena trieda ma dalej k dispozicii metody, ktorymi moze zahajit cakanie
	 * na vsetky vstupne sihnaly {@link #wait4SignalsIn} alebo odoslat vsetky 
	 * vystupne signaly {@link sendSignalsOut}. 
	 * 
	 * @tparam BoxInterface definicia IO rozhrania boxu. 
	 * 
	 **/
	//template <class BoxInterface>
	class Box : public bobox::basic_box2 {
	public:
		
		class Group {
			typedef bobox::basic_box2::group GroupImpl;
		public: 
			Group(const GroupImpl* g): impl_(g) { }

			size_t getReadBegin() const { return impl_->rdbegin_; }
			void setReadBegin(size_t val) { impl_->rdbegin_ = val; }
			void moveReadBegin(int val = 1) { impl_->rdbegin_ += val; }

			size_t getReadPosition() const { return impl_->rdpos_; }
			void setReadPosition(size_t val) { impl_->rdpos_ = val; }
			void moveReadPosition(int val = 1) { impl_->rdpos_ += val; }

			size_t getReadEnd() const { return impl_->rdend_; }
			void setReadEnd(size_t val) { impl_->rdend_ = val; }
			void moveReadEnd(int val) { impl_->rdend_ += val; }

			size_t getWriteBegin() const { return impl_->wrbegin_; }
			void setWriteBegin(size_t val) { impl_->wrbegin_ = val; }
			void moveWriteBegin(int val) { impl_->wrbegin_ += val; }

			size_t getWritePosition() const { return impl_->wrpos_; }
			void setWritePosition(size_t val) { impl_->wrpos_ = val; }
			void moveWritePosition(int val) { impl_->wrpos_ += val; }

			size_t getWriteEnd() const { return impl_->wrend_; }
			void setWriteEnd(size_t val) { impl_->wrend_ = val; }
			void moveWriteEnd(int val) { impl_->wrend_ += val; }

			bool isPoisoned() const { return impl_->poisoned_; }

			size_t getColumnCount() const { return impl_->columns_.size(); }
		private:
			const GroupImpl* impl_;
		};

		Group get_group(groupType idx) const {
			return Group(&bobox::basic_box2::get_group(idx));
		}
		/// IO rozhranie Boxu
		//typedef BoxInterface BoxIO;

		/**
		 * @param id identifikacia instancie  boxu predana z jadra frameworku 
		 * pri vytvoreni boxu
		 * 
		 * @param tid identifikacia typu boxu
		 * 
		 * @param req ???
		 * 
		 * @param params mapa parametrov predana boxu v textovej defi nicii 
		 * modelu vypoctu
		 * 
		 * @param forceSignalInCount nasilne prepisanie hodnoty parametra 
		 * SIGNAL_IN_PARAMETER_NAME z definicie txt. modelu. Zaporna 
		 * hodnota znaci, ze sa ma pouzivat definicia z modelu. Kladna hodnota
		 * znamena prepisanie hodnoty parametra.
		 * 
		 * @param forceSignalOutCount nasilne prepisanie hodnoty parametra 
		 * SIGNAL_OUT_PARAMETER_NAME z definicie txt. modelu. Zaporna 
		 * hodnota znaci, ze sa ma pouzivat definicia z modelu. Kladna hodnota
		 * znamena prepisanie hodnoty parametra.
		 **/
		Box(bobox::box_id_type id, bobox::box_tid_type tid, 
			bobox::request* req, const bobox::parameters& params, 
			int forceSignalInCount = -1, int forceSignalOutCount = -1);

		/// Pomenovanie boxu (typu) pouzivane v txt. modele vypoctu.
		//std::string getBoxName() const { return BoxIO::BOX_ID; }
		
		/// Jedinecne id boxu v ramci jeho typu. Urcene pre ladenie.
		int getBoxInstanceId() const { return boxInstanceId_; }

	protected:		
		
		/**
		 * Inicializacia boxu volana medzi konstrukciou a spustenim vypoctu boxu.
		 * Box si moze nastavit vlastne skupiny vstupov/vystupov a zahajit 
		 * cakanie na vstup.
		 **/
		virtual void initImpl() = 0;

		/**
		 * Posle vsetky vystupne signaly. 
		 **/
		void sendSignalsOut() {
			for (int i = 0; i < signalOutCount_; ++i) {
				send(BaseBoxDescriptor::getSignalOutArc(i),
					group_index_type(BaseBoxDescriptor::getSignalOutGroup(i)));
			}
		}

		/**
		 * Zahaji cakanie na vsetky vstupne signaly.
		 **/
		void wait4SignalsIn() {
			for (int i = 0; i < signalInCount_; ++i) {
				receive(BaseBoxDescriptor::getSignalInArc(i), 
					BaseBoxDescriptor::getSignalInGroup(i));
			}
		}  

		/**
		 * Nacitanie boolovskeho parametru boxu z definicie v modele zadaneho 
		 * pomocou cisla (0 == false, inak true).
		 * 
		 * @param paramName meno nacitaneho parametra v modele vypoctu
		 *
		 * @param defaultValue hodnota, ktora sa pouzije ak sa parameter
		 * v mdodele nenachadza (volitelna, defaultne je to false)
		 **/
		bool loadBoxBoolParameter(const bobox::parameters& params, 
			const std::string& paramName, 
			const bool defaultValue = false) const {
			
				if (params.has_parameter(paramName)) {
					int value = defaultValue ? 1 : 0;
					params.get_parameter(paramName, value);
					return value != 0;
				}
				else {
					return defaultValue;
				}

		}

		/**
		 * Nacitanie parametru boxu, z definicie v modele. 
		 * 
		 * @param params mapa parametrov boxu
		 * 
		 * @param paramName meno nacitaho parametra
		 * 
		 * @param defaultValue defaultna hodnota parametra, ktora sa pouzije
		 * pokial sa parameter nenachadza v stupnej mape
		 * 
		 * @tparam ValueType typ nacitaneho parametra
		 **/
		template<typename ValueType>
		ValueType getBoxParameterValue(const bobox::parameters& params, 
			const std::string& paramName, 
			const ValueType& defaultValue) const {

				ValueType value = defaultValue;
				if (params.has_parameter(paramName)) {
					params.get_parameter(paramName, value);
				}
				return value;
		}

		/// Nastavenie poctu vstpnych signalov fungueje do a pocas zavolania 
		/// metody {@link #initImpl()}, po inicializcii uz zmena nastavenie 
		/// ziaden vplyv.
		void setSignalsIn(int value) { signalInCount_ = value; }

		/// Nastavenie vystpneho signalu fungueje do a pocas zavolania 
		/// metody {@link #initImpl()}, po inicializcii uz zmena nastavenie 
		/// ziaden vplyv.
		void setSignalsOut(int value) { signalOutCount_ = value; }
	private:
		/// Citac id boxov pre generovanie unikatnych id.
		static int nextBoxInstanceId_;
		/// Unikatny identifikator boxu - vhodny len pre ladenie.
		/// Id nie je unikatne v ramci celeho modelu ale len v ramci skupiny
		/// boxov s rovnakym IO rozhranim, pretoze rozhranie je sablonovy 
		/// parameter a pre kazde rozne rozhranie je vytvorena vlastna rodicovska 
		/// trieda z ktorej sa dedi aj id instancie boxu.
		int boxInstanceId_;
		/// Pocet vstupnych signalov do boxu.
		int signalInCount_;
		/// Pocet vystupnych signalov z boxu.
		int signalOutCount_;

		/**
		 * Inicializacia skupin pre vstupne signaly.
		 * Kazdy signal ma prave jednu skupinu. Indexy skupiny a parametrov 
		 * spojenia su urcene v triede {@link BaseBoxDescriptor}, konkretne 
		 * {@link BaseBoxDescriptor::getSignalInGroup(int)} a 
		 * {@link BaseBoxDescriptor::getSignalInArc(int)}
		 **/
		void initSignalInGroup() {
			for (int i = 0; i < signalInCount_; ++i) {
				add_group(BaseBoxDescriptor::getSignalInGroup(i), 
					BaseBoxDescriptor::SIGNAL_IN_COLUMN, 
					get_input_descriptors(BaseBoxDescriptor::getSignalInArc(i)));
			}
		}

		/**
		 * Inicializacia skupin pre vystupne signaly.
		 * Kazdy signal ma prave jednu skupinu. Indexy skupiny a parametrov 
		 * spojenia su urcene v triede {@link BaseBoxDescriptor}, konkretne 
		 * {@link BaseBoxDescriptor::getSignalOutGroup(int)} a 
		 * {@link BaseBoxDescriptor::getSignalOutArc(int)}
		 **/
		void initSignalOutGroup() {
				for (int i = 0; i < signalOutCount_; ++i) {
				add_group(BaseBoxDescriptor::getSignalOutGroup(i), 
					BaseBoxDescriptor::SIGNAL_OUT_COLUMN, 
					get_output_descriptors(BaseBoxDescriptor::getSignalOutArc(i)));
			}
		}

		/**
		 * Inicializacia skupin pre vstupne aj vystupne signaly.
		 * Pre inicializaciu zdedeneho boxu je urcena metoda {@link initImpl()}.
		 **/
		virtual void init_impl() override {
			initImpl();
			// inicializacia signalov in a out az po zavolani initImpl(), tak 
			// aby mali potomkovia v pripade potreby moznost nastavenie 
			// este niekedy premastit.
			initSignalInGroup();
			initSignalOutGroup();
		}
	};

	//template <class BoxInterface>
	//	int jm4bbx::Box<BoxInterface>::nextBoxInstanceId_ = 0;

} // namespace jm4bbx

#endif
